package com.xiaoyu.im.datamodel.bridge;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import android.text.TextUtils;

import com.netease.nimlib.sdk.msg.MessageBuilder;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.netease.nimlib.sdk.msg.constant.MsgStatusEnum;
import com.netease.nimlib.sdk.msg.model.CustomNotification;
import com.netease.nimlib.sdk.msg.model.IMMessage;
import com.netease.nimlib.sdk.msg.model.MessageReceipt;
import com.xiaoyu.base.entity.ConversationEntity;
import com.xiaoyu.im.datamodel.ReceiveType;
import com.xiaoyu.im.datamodel.common.IMConstant;
import com.xiaoyu.im.client.proxy.nim.NimProxy;
import com.xiaoyu.im.provider.ConversationManager;
import com.xiaoyu.base.DBCall;
import com.xiaoyu.base.entity.MessageEntity;
import com.xiaoyu.base.data.UserData;
import com.xiaoyu.im.IMCallback;
import com.xiaoyu.im.IMLog;
import com.xiaoyu.im.IMUtil;
import com.xiaoyu.im.client.proxy.nim.CustomAttachment;
import com.xiaoyu.im.datamodel.AppMessage;
import com.xiaoyu.im.datamodel.MessageIdType;
import com.xiaoyu.im.datamodel.MessagePayload;
import com.xiaoyu.im.datamodel.MessageStatus;
import com.xiaoyu.im.datamodel.ReadReceipt;
import com.xiaoyu.im.datamodel.exception.IMException;
import com.xiaoyu.im.datamodel.vendors.NimConversationUnique;
import com.xiaoyu.im.datamodel.vendors.VendorConversationUnique;
import com.xiaoyu.im.provider.ChatIdMapper;
import com.xiaoyu.im.provider.MessageReceiptData;
import com.xiaoyu.base.utils.ListUtil;

import java.util.List;
import java.util.Objects;

import in.srain.cube.request.JsonData;
import in.srain.cube.util.CLog;
import in.srain.cube.concurrent.AppThreads;

public class NimMessageBridge implements MessageBridge<IMMessage, NimConversationUnique, CustomNotification> {

    private static final String TAG = "NimMessageBridge";
    private final NimProxy mNimProxy;

    public NimMessageBridge(NimProxy nimProxy) {
        this.mNimProxy = nimProxy;
    }

    @NonNull
    @Override
    public IMMessage create(@NonNull AppMessage appMessage) {
        if (appMessage == null) {
            throw new IllegalArgumentException("appMessage is null");
        }
        final String chatId = appMessage.chatToken.getChatId();
        if (TextUtils.isEmpty(chatId)) {
            throw new IllegalStateException("cannot found chat id in AppMessage");
        }
        final NimConversationUnique unique = toConversationUnique(chatId);
        if (unique == null) {
            throw new IllegalStateException("cannot found unique by chat id");
        }

        final JsonData messagePayloadJsonData = appMessage.payload.toJsonData();
        final CustomAttachment attachment = CustomAttachment.fromJsonData(messagePayloadJsonData);
        final IMMessage message = MessageBuilder.createCustomMessage(unique.vendorCid, unique.sessionType, appMessage.payload.getText(), attachment);

        final String fromAccount = message.getFromAccount();
        final String vendorUid = mNimProxy.getSuccessIMTokenInfo().vendorUid;
        if (TextUtils.isEmpty(fromAccount) && !TextUtils.isEmpty(vendorUid)) { // fix sdk
            message.setFromAccount(vendorUid);
        }

        if (appMessage.shouldSetServerForbidden()) {
            message.setClientAntiSpam(true);
        }
        CLog.d(IMConstant.LOG_TAG, "%s create %s -> %s", TAG, appMessage, stringOfNimMessage(message));
        return message;
    }

    @Override
    public void recreate(AppMessage appMessage, IMCallback<IMMessage> callback) {
        mNimProxy.queryNimMessagesByUUID(appMessage.id, callback);
    }

    @NonNull
    public AppMessage tryToAppMessage(@NonNull IMMessage message) throws IllegalStateException, IllegalArgumentException {
        if (message == null) {
            throw new IllegalArgumentException("message is null");
        }
        final NimConversationUnique unique = toConversationUnique(message);
        if (unique == null) {
            throw new IllegalStateException("to unique is null");
        }
        final String chatId = ChatIdMapper.getInstance().getChatIdByVendor(unique);
        if (TextUtils.isEmpty(chatId)) {
            throw new IllegalStateException("chat id is empty, unique=" + unique);
        }

        return tryToAppMessage(message, chatId);
    }

    @NonNull
    @Override
    public AppMessage tryToAppMessage(IMMessage message, String chatId) throws IllegalStateException {
        VendorConversationUnique unique = toConversationUnique(message);
        if (unique == null) {
            throw new IllegalStateException("to unique is null");
        }

        final MsgAttachment attachment = message.getAttachment();
        final JsonData messagePayloadJsonData = attachment instanceof CustomAttachment ? ((CustomAttachment) attachment).toJsonData() : JsonData.newMap();
        final MessagePayload payload = MessagePayload.fromJsonData(messagePayloadJsonData);
        final List<String> mentionList = payload.getMentionList();

        final String uid = UserData.getInstance().getUid();
        final String senderUid = IMUtil.vendorCidToUid(message.getFromAccount());
        final String targetUid = IMUtil.vendorCidToUid(message.getSessionId());
        final String id = message.getUuid();
        final long time = message.getTime();
        final boolean isReadReceipt = message.isRemoteRead() || MessageReceiptData.getInstance().isReceiptTime(chatId, time);
        final int readReceipt = isReadReceipt ? ReadReceipt.READ : ReadReceipt.UNREAD;

        MessageEntity entity = DBCall.call(database -> database.messageDao().getMessage(id));
        if (entity == null) {
            entity = new MessageEntity();
            entity.setDisplayTime(time);
        }
        entity.setId(id);
        entity.setIdType(MessageIdType.INT_NIM);
        entity.setChatId(chatId);
        entity.setCid(message.getSessionId());
        entity.setContent(payload.toString());
        entity.setPayloadVersion(payload.getCurrentVersion());
        entity.setIoType(TextUtils.equals(senderUid, uid) ? 2 : 1); // remove
        entity.setStatus(message.getStatus() == MsgStatusEnum.success ? MessageStatus.SUCCESS : MessageStatus.SDK_FAILED);
        entity.setSenderUid(senderUid);
        entity.setTargetUid(targetUid);
        entity.setTime(time);
        entity.setType(payload.getMessageType());
        entity.setMentionAll(payload.isMentionAll());
        entity.setLocalId(id); // temp
        entity.setInvisible(payload.isInvisible(uid));
        entity.setInvisibleLastContent(payload.isInvisibleLastContent(uid));
        if (entity.getReadReceipt() != ReadReceipt.READ) {
            entity.setReadReceipt(readReceipt);
        }
        if (!ListUtil.isNullOrEmpty(mentionList)) {
            entity.setMentionListString(JsonData.create(mentionList).toString());
        }

        final AppMessage appMessage = AppMessage.fromEntity(entity);
        CLog.d(IMConstant.LOG_TAG, "%s toAppMessage %s -> %s", TAG, stringOfNimMessage(message), appMessage);
        return appMessage;
    }

    @NonNull
    @Override
    public AppMessage tryNotificationToAppMessage(CustomNotification notification, String chatId) throws IllegalStateException {
        VendorConversationUnique unique = notificationToConversationUnique(notification);
        if (unique == null) {
            throw new IllegalStateException("to unique is null");
        }

        final JsonData contentJsonData = JsonData.create(notification.getContent());
        final MessagePayload payload = MessagePayload.fromJsonData(contentJsonData);
        final List<String> mentionList = payload.getMentionList();

        final String uid = UserData.getInstance().getUid();
        final String senderUid = IMUtil.vendorCidToUid(notification.getFromAccount());
        final String targetUid = IMUtil.vendorCidToUid(notification.getSessionId());
        final String id = String.valueOf(Objects.hash(unique, notification.getFromAccount(), notification.getTime(), notification.getContent()));
        final long time = notification.getTime();

        MessageEntity entity = new MessageEntity();
        entity.setId(id);
        entity.setDisplayTime(time);
        entity.setIdType(MessageIdType.INT_NIM);
        entity.setChatId(chatId);
        entity.setCid(notification.getSessionId());
        entity.setContent(payload.toString());
        entity.setPayloadVersion(payload.getCurrentVersion());
        entity.setStatus(MessageStatus.SUCCESS);
        entity.setSenderUid(senderUid);
        entity.setTargetUid(targetUid);
        entity.setTime(time);
        entity.setType(payload.getMessageType());
        entity.setMentionAll(payload.isMentionAll());
        entity.setLocalId(id); // temp
        entity.setInvisible(payload.isInvisible(uid));
        entity.setInvisibleLastContent(payload.isInvisibleLastContent(uid));
        if (!ListUtil.isNullOrEmpty(mentionList)) {
            entity.setMentionListString(JsonData.create(mentionList).toString());
        }

        final AppMessage appMessage = AppMessage.fromEntity(entity);
        CLog.d(IMConstant.LOG_TAG, "%s toAppMessage %s -> %s", TAG, stringOfNimCustomNotification(notification), appMessage);
        return appMessage;
    }

    @Nullable
    @Override
    public AppMessage toAppMessageOrNull(@Nullable IMMessage message) {
        try {
            return tryToAppMessage(message);
        } catch (Throwable e) {
            IMLog.logToAppMessageError("recall_or_query", message, e);
            return null;
        }
    }

    @Override
    public void toAppMessage(@NonNull IMMessage message, IMCallback<AppMessage> callback) {
        AppThreads.runOnIOThread(() -> {
            try {
                callback.onResult(true, tryToAppMessage(message), null);
            } catch (Throwable e) {
                IMLog.logToAppMessageError("send", message, e);
                callback.onResult(false, null, new IMException(e));
            }
        });
    }

    @Nullable
    @Override
    public NimConversationUnique toConversationUnique(IMMessage message) {
        return NimConversationUnique.createOrNull(message.getSessionId(), message.getSessionType());
    }

    @Nullable
    @Override
    public NimConversationUnique toConversationUnique(String chatId) {
        ConversationEntity conversation = ConversationManager.getInstance().fetchConversation(chatId);
        return conversation != null ? NimConversationUnique.createOrNull(conversation.getToUserAccid(), conversation.getType()) : null;
    }

    @Nullable
    @Override
    public NimConversationUnique notificationToConversationUnique(CustomNotification notification) {
        return NimConversationUnique.createOrNull(notification.getSessionId(), notification.getSessionType());
    }

    public static String stringOfListNimMessageForReceiveMessage(List<IMMessage> messages) {
        StringBuilder sb = new StringBuilder();
        for (IMMessage message : messages) {
            String messageString = stringOfNimMessage(message);
            String senderUid = IMUtil.vendorCidToUid(message.getFromAccount());
            IMLog.logImStreamForObserveReceiveMessage(ReceiveType.MESSAGE, senderUid, message.getUuid(), messageString, message.getTime());

            sb.append(messageString);
            sb.append(" ");
        }
        return sb.toString().trim();
    }

    public static String stringOfListNimMessage(List<IMMessage> messages) {
        StringBuilder sb = new StringBuilder();
        for (IMMessage message : messages) {
            sb.append(stringOfNimMessage(message));
            sb.append(" ");
        }
        return sb.toString().trim();
    }

    public static String stringOfNimMessage(IMMessage message) {
        return "IMMessage{" +
                "getUuid=" + message.getUuid() +
                ", getTime=" + message.getTime() +
                ", getFromAccount=" + message.getFromAccount() +
                ", getContent=" + message.getContent() +
                ", getStatus=" + message.getStatus() +
                ", getMsgType=" + message.getMsgType() +
                ", getSessionId=" + message.getSessionId() +
                ", getFromNick=" + message.getFromNick() +
                ", getRemoteExtension=" + message.getRemoteExtension() +
                ", getConfig=" + message.getConfig() +
                ", getPushContent=" + message.getPushContent() +
                ", getAttachment=" + message.getAttachment() +
                ", getDirect=" + message.getDirect() +
                ", getFromClientType=" + message.getFromClientType() +
                ", getLocalExtension=" + message.getLocalExtension() +
                ", getMemberPushOption=" + message.getMemberPushOption() +
                ", getNIMAntiSpamOption=" + message.getNIMAntiSpamOption() +
                ", getPushPayload=" + message.getPushPayload() +
                ", getSessionType=" + message.getSessionType() +
                ", isRemoteRead=" + message.isRemoteRead() +
                '}';
    }

    public static String stringOfNimCustomNotificationForReceiveNotification(CustomNotification notification) {
        String messageString = stringOfNimCustomNotification(notification);
        String senderUid = IMUtil.vendorCidToUid(notification.getFromAccount());
        IMLog.logImStreamForObserveReceiveMessage(ReceiveType.NOTIFICATION, senderUid, "", messageString, notification.getTime());
        return messageString;
    }

    public static String stringOfNimCustomNotification(CustomNotification notification) {
        return "IMMessage{" +
                "getSessionId=" + notification.getSessionId() +
                ", getServerTime=" + notification.getTime() +
                ", getFromAccount=" + notification.getFromAccount() +
                ", getContent=" + notification.getContent() +
                ", isSendToOnlineUserOnly=" + notification.isSendToOnlineUserOnly() +
                ", getApnsText=" + notification.getApnsText() +
                ", getConfig=" + notification.getConfig() +
                ", getNIMAntiSpamOption=" + notification.getNIMAntiSpamOption() +
                ", getPushPayload=" + notification.getPushPayload() +
                ", getSessionType=" + notification.getSessionType() +
                '}';
    }

    public static String stringOfListMessageReceipt(List<MessageReceipt> messageReceipts) {
        StringBuilder sb = new StringBuilder();
        for (MessageReceipt messageReceipt : messageReceipts) {
            sb.append(stringOfMessageReceipt(messageReceipt));
            sb.append(" ");
        }
        return sb.toString().trim();
    }

    public static String stringOfMessageReceipt(MessageReceipt messageReceipt) {
        return "MessageReceipt{" +
                "getSessionId=" + messageReceipt.getSessionId() +
                ", getServerTime=" + messageReceipt.getTime() +
                '}';
    }
}
